﻿#include "app_usb.h"

#include "app_cdc.h"
#include "app_dap.h"
#include "dap_config.h"
#include "gd32f4xx_libopt.h"
#include "ulog.h"
#include "usbd_cdc.h"
#include "usbd_core.h"

/* 外部变量 */
extern void USBD_IRQHandler(uint8_t busid); /* CherryUSB的处理函数 */
extern char dap_uinque_serial_number[32];   /* DAP唯一序列号 */

/* 接口描述符长度 */
#define DAP_INTERFACE_SIZE ((9 + 7 + 7) + (7 * SWO_UART_SUPPORT))
#define CDC_INTERFACE_SIZE (CDC_ACM_DESCRIPTOR_LEN)

/* 配置描述符总长度 */
#define USB_CONFIG_SIZE (9 + DAP_INTERFACE_SIZE + CDC_INTERFACE_SIZE)
#define INTF_NUM (1 + 2) /* WINUSB*1 + CDC*2 */

#ifdef CONFIG_USB_HS
#if DAP_PACKET_SIZE != 512
#error "DAP_PACKET_SIZE must be 512 in hs"
#endif
#else /* CONFIG_USB_HS */
#if DAP_PACKET_SIZE != 64
#error "DAP_PACKET_SIZE must be 64 in fs"
#endif
#endif /* !CONFIG_USB_HS */

#define USBD_WINUSB_VENDOR_CODE 0x20
#define FUNCTION_SUBSET_LEN (160)
#define DEVICE_INTERFACE_GUIDS_FEATURE_LEN 132

/* WINUSB描述符长度 */
#define USBD_WINUSB_DESC_SET_LEN (WINUSB_DESCRIPTOR_SET_HEADER_SIZE + FUNCTION_SUBSET_LEN)

// clang-format off

/* BOS描述符 */
static const uint8_t usbd_msosv2_descriptor_raw[] = {
    WBVAL(WINUSB_DESCRIPTOR_SET_HEADER_SIZE),  /* wLength */
    WBVAL(WINUSB_SET_HEADER_DESCRIPTOR_TYPE),  /* wDescriptorType */
    0x00, 0x00, 0x03, 0x06,                    /* dwWindowsVersion*/
    WBVAL(USBD_WINUSB_DESC_SET_LEN),           /* wDescriptorSetTotalLength */

    WBVAL(WINUSB_FUNCTION_SUBSET_HEADER_SIZE), /* wLength */
    WBVAL(WINUSB_SUBSET_HEADER_FUNCTION_TYPE), /* wDescriptorType */
    0,                                         /* bFirstInterface */
    0,                                         /* bReserved */
    WBVAL(FUNCTION_SUBSET_LEN),                /* wSubsetLength */

    WBVAL(WINUSB_FEATURE_COMPATIBLE_ID_SIZE),  /* wLength */
    WBVAL(WINUSB_FEATURE_COMPATIBLE_ID_TYPE),  /* wDescriptorType */
    'W', 'I', 'N', 'U', 'S', 'B', 0, 0,        /* CompatibleId*/
    0, 0, 0, 0, 0, 0, 0, 0,                    /* SubCompatibleId*/
    WBVAL(DEVICE_INTERFACE_GUIDS_FEATURE_LEN), /* wLength */
    WBVAL(WINUSB_FEATURE_REG_PROPERTY_TYPE),   /* wDescriptorType */
    WBVAL(WINUSB_PROP_DATA_TYPE_REG_MULTI_SZ), /* wPropertyDataType */
    WBVAL(42),                                 /* wPropertyNameLength */
    'D', 0, 'e', 0, 'v', 0, 'i', 0, 
    'c', 0, 'e', 0, 'I', 0, 'n', 0, 
    't', 0, 'e', 0, 'r', 0, 'f', 0, 
    'a', 0, 'c', 0, 'e', 0, 'G', 0, 
    'U', 0, 'I', 0, 'D', 0, 's', 0,
    0, 0, 
    WBVAL(80), /* wPropertyDataLength */
    '{', 0, 'C', 0, 'D', 0, 'B', 0, 
    '3', 0, 'B', 0, '5', 0, 'A', 0, 
    'D', 0, '-', 0, '2', 0, '9', 0, 
    '3', 0, 'B', 0, '-', 0, '4', 0, 
    '6', 0, '6', 0, '3', 0, '-', 0, 
    'A', 0, 'A', 0, '3', 0, '6', 0, 
    '-', 0, '1', 0, 'A', 0, 'A', 0, 
    'E', 0, '4', 0, '6', 0, '4', 0, 
    '6', 0, '3', 0, '7', 0, '7', 0, 
    '6', 0, '}', 0,
    0, 0, 
    0, 0
};
// clang-format on

#define USBD_NUM_DEV_CAPABILITIES (1)
#define USBD_WINUSB_DESC_LEN 28U

/* BOS描述符长度 */
#define USBD_BOS_WTOTALLENGTH (0x05 + USBD_WINUSB_DESC_LEN)

// clang-format off

static const uint8_t usbd_bos_descriptor_raw[] = {
    0x05,                         /* bLength */
    0x0f,                         /* bDescriptorType */
    WBVAL(USBD_BOS_WTOTALLENGTH), /* wTotalLength */
    USBD_NUM_DEV_CAPABILITIES,    /* bNumDeviceCaps */

    USBD_WINUSB_DESC_LEN,           /* bLength */
    0x10,                           /* bDescriptorType */
    USB_DEVICE_CAPABILITY_PLATFORM, /* bDevCapabilityType */
    0x00,                           /* bReserved */
    0xDF, 0x60, 0xDD, 0xD8,         /* PlatformCapabilityUUID */
    0x89, 0x45, 0xC7, 0x4C, 
    0x9C, 0xD2, 0x65, 0x9D, 
    0x9E, 0x64, 0x8A, 0x9F,
    0x00, 0x00, 0x03, 0x06,          /* dwWindowsVersion*/
    WBVAL(USBD_WINUSB_DESC_SET_LEN), /* wDescriptorSetTotalLength */
    USBD_WINUSB_VENDOR_CODE,         /* bVendorCode */
    0                                /* bAltEnumCode */
};
// clang-format on

static struct usb_msosv2_descriptor usbd_msosv2_descriptor = {  //
    .vendor_code = USBD_WINUSB_VENDOR_CODE,                     //
    .compat_id = usbd_msosv2_descriptor_raw,                    //
    .compat_id_len = USBD_WINUSB_DESC_SET_LEN};

static struct usb_bos_descriptor usbd_bos_descriptor = {  //
    .string = usbd_bos_descriptor_raw,                    //
    .string_len = USBD_BOS_WTOTALLENGTH};

/* 设备描述符 */
static const uint8_t usbd_device_descriptor[] = {  //
    USB_DEVICE_DESCRIPTOR_INIT(                    //
        USB_2_1,                                   /* bcdUSB */
        0xEF,                                      /* bDeviceClass */
        0x02,                                      /* bDeviceSubClass */
        0x01,                                      /* bDeviceProtocol */
        USBD_VID,                                  /* idVendor */
        USBD_PID,                                  /* idProduct */
        0x0100,                                    /* bcdDevice */
        0x01                                       /* bNumConfigurations */
        )};

/* 配置描述符 */
static const uint8_t usbd_config_descriptor[] = {
    /* Configuration 0 9bytes */
    USB_CONFIG_DESCRIPTOR_INIT(  //
        USB_CONFIG_SIZE,         /* wTotalLength */
        INTF_NUM,                /* bNumInterfaces */
        0x01,                    /* bConfigurationValue */
        USB_CONFIG_BUS_POWERED,  /* bmAttributes */
        USBD_MAX_POWER           /* bMaxPower */
        ),

    /* Interface 0 9bytes */
    USB_INTERFACE_DESCRIPTOR_INIT(  //
        0x00,                       /* bInterfaceNumber */
        0x00,                       /* bAlternateSetting */
        0x02,                       /* bNumEndpoints */
        0xFF,                       /* bInterfaceClass */
        0x00,                       /* bInterfaceSubClass */
        0x00,                       /* bInterfaceProtocol */
        0x02                        /* iInterface */
        ),

    /* Endpoint OUT 7bytes */
    USB_ENDPOINT_DESCRIPTOR_INIT(  //
        DAP_OUT_EP,                /* bEndpointAddress, */
        USB_ENDPOINT_TYPE_BULK,    /* bmAttributes */
        DAP_PACKET_SIZE,           /* wMaxPacketSize */
        0x00                       /* bInterval */
        ),

    /* Endpoint IN 7bytes */
    USB_ENDPOINT_DESCRIPTOR_INIT(  //
        DAP_IN_EP,                 /* bEndpointAddress, */
        USB_ENDPOINT_TYPE_BULK,    /* bmAttributes */
        DAP_PACKET_SIZE,           /* wMaxPacketSize */
        0x00                       /* bInterval */
        ),

#if (SWO_UART_SUPPORT)
    /* Endpoint IN 7bytes */
    USB_ENDPOINT_DESCRIPTOR_INIT(  //
        SWO_IN_EP,                 /* bEndpointAddress, */
        USB_ENDPOINT_TYPE_BULK,    /* bmAttributes */
        DAP_PACKET_SIZE,           /* wMaxPacketSize */
        0x00                       /* bInterval */
        ),
#endif

    /* CDC */
    CDC_ACM_DESCRIPTOR_INIT(  //
        0x01,                 /*  */
        CDC_INT_EP,           /*  */
        CDC_OUT_EP,           /*  */
        CDC_IN_EP,            /*  */
        DAP_PACKET_SIZE,      /*  */
        0x00                  /*  */
        )};

/* 设备限定描述符 */
static const uint8_t usbd_device_quality_descriptor[] = {  //
    USB_DEVICE_QUALIFIER_DESCRIPTOR_INIT(                  //
        USB_2_1,                                           /* bcdUSB */
        0x00,                                              /* bDeviceClass */
        0x00,                                              /* bDeviceSubClass */
        0x00,                                              /* bDeviceProtocol */
        0x00                                               /* bNumConfigurations */
        )};

/* 字符串描述符 */
static const char *usbd_string_descriptors[] = {  //
    (const char[]){WBVAL(USBD_LANGID_STRING)},    /**/
    CMSIS_DAP_VENDOR_NAME,                        /**/
    CMSIS_DAP_PRODUCT_NAME,                       /**/
    dap_uinque_serial_number};

static const uint8_t *device_descriptor_callback(uint8_t speed) {
    if (speed == USB_SPEED_HIGH) {
        return usbd_device_descriptor;
    } else {
        return NULL;
    }
}

static const uint8_t *config_descriptor_callback(uint8_t speed) {
    if (speed == USB_SPEED_HIGH) {
        return usbd_config_descriptor;
    } else {
        return NULL;
    }
}

static const uint8_t *device_quality_descriptor_callback(uint8_t speed) {
    if (speed == USB_SPEED_HIGH) {
        return usbd_device_quality_descriptor;
    } else {
        return NULL;
    }
}

static const uint8_t *other_speed_descriptor_callback(uint8_t speed) {
    return NULL;  //
}

static const char *string_descriptor_callback(uint8_t speed, uint8_t index) {
    if (index > 3) {
        return NULL;
    }
    return usbd_string_descriptors[index];
}

/* 描述符汇总 */
static struct usb_descriptor usbd_desc = {
    .device_descriptor_callback = device_descriptor_callback,
    .config_descriptor_callback = config_descriptor_callback,
    .device_quality_descriptor_callback = device_quality_descriptor_callback,
    .other_speed_descriptor_callback = other_speed_descriptor_callback,
    .string_descriptor_callback = string_descriptor_callback,
    .msosv1_descriptor = NULL,                    /* MS V1 */
    .msosv2_descriptor = &usbd_msosv2_descriptor, /* MS V2 */
    .webusb_url_descriptor = NULL,                /* WEB */
    .bos_descriptor = &usbd_bos_descriptor        /* BOS */
};

static struct usbd_interface dap_intf;
static struct usbd_interface cdc_intf1;
static struct usbd_interface cdc_intf2;

static struct usbd_endpoint dap_out_ep = {.ep_addr = DAP_OUT_EP, .ep_cb = APP_DAP_OutCallback};
static struct usbd_endpoint dap_in_ep = {.ep_addr = DAP_IN_EP, .ep_cb = APP_DAP_InCallback};
static struct usbd_endpoint cdc_out_ep = {.ep_addr = CDC_OUT_EP, .ep_cb = APP_CDC_OutCallback};
static struct usbd_endpoint cdc_in_ep = {.ep_addr = CDC_IN_EP, .ep_cb = APP_CDC_InCallback};

/**
 * @brief USB事件回调，CherryUSB内部调用
 *
 * @param busid
 * @param event
 */
void usbd_event_handler(uint8_t busid, uint8_t event) {
    switch (event) {
        case USBD_EVENT_RESET:
            break;
        case USBD_EVENT_CONNECTED:
            break;
        case USBD_EVENT_DISCONNECTED:
            break;
        case USBD_EVENT_RESUME:
            break;
        case USBD_EVENT_SUSPEND:
            break;
        case USBD_EVENT_CONFIGURED:
            APP_DAP_StartRead();
            APP_CDC_StartRead();
            break;
        case USBD_EVENT_SET_REMOTE_WAKEUP:
            break;
        case USBD_EVENT_CLR_REMOTE_WAKEUP:
            break;

        default:
            break;
    }
}

/**
 * @brief 开USB中断，CherryUSB内部调用
 *
 * @param busid 0
 */
void usb_dc_low_level_init(uint8_t busid) {
    if (busid == 0) {
        nvic_irq_enable(USBHS_IRQn, 1, 0);
    }
}

/**
 * @brief 关USB中断，CherryUSB内部调用
 *
 * @param busid 0
 */
void usb_dc_low_level_deinit(uint8_t busid) {
    if (busid == 0) {
        nvic_irq_disable(USBHS_IRQn);
    }
}

/**
 * @brief USB中断函数，套接到CherryUSB的处理函数
 *
 */
void USBHS_IRQHandler(void) {
    USBD_IRQHandler(0);  //
}

/**
 * @brief 初始化USB外设
 *
 */
void APP_USB_Init(void) {
    /* USBPHY复位脚 */
    gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
    gpio_bit_reset(GPIOC, GPIO_PIN_7);

    /* MCO 24MHz作为PHY的晶振  */
    rcu_ckout1_config(RCU_CKOUT1SRC_HXTAL, 1); /* 直接输出24M晶振 */
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
    gpio_af_set(GPIOC, GPIO_AF_0, GPIO_PIN_9);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_9);

    /* ULPI IOs */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);

    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_5);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_3);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_2);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_0);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_5);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_13);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_12);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_11);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_10);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_1);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_0);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_3);

    gpio_af_set(GPIOA, GPIO_AF_10, GPIO_PIN_5);
    gpio_af_set(GPIOC, GPIO_AF_10, GPIO_PIN_3);
    gpio_af_set(GPIOC, GPIO_AF_10, GPIO_PIN_2);
    gpio_af_set(GPIOC, GPIO_AF_10, GPIO_PIN_0);
    gpio_af_set(GPIOB, GPIO_AF_10, GPIO_PIN_5);
    gpio_af_set(GPIOB, GPIO_AF_10, GPIO_PIN_13);
    gpio_af_set(GPIOB, GPIO_AF_10, GPIO_PIN_12);
    gpio_af_set(GPIOB, GPIO_AF_10, GPIO_PIN_11);
    gpio_af_set(GPIOB, GPIO_AF_10, GPIO_PIN_10);
    gpio_af_set(GPIOB, GPIO_AF_10, GPIO_PIN_1);
    gpio_af_set(GPIOB, GPIO_AF_10, GPIO_PIN_0);
    gpio_af_set(GPIOA, GPIO_AF_10, GPIO_PIN_3);

    /* 60MHz from PHY to USBHS */
    rcu_periph_clock_enable(RCU_USBHSULPI);
    rcu_periph_clock_enable(RCU_USBHS);

    usbd_desc_register(0, &usbd_desc);

    /*!< winusb */
    usbd_add_interface(0, &dap_intf);
    usbd_add_endpoint(0, &dap_out_ep);
    usbd_add_endpoint(0, &dap_in_ep);

    /* CDC */
    usbd_add_interface(0, usbd_cdc_acm_init_intf(0, &cdc_intf1));
    usbd_add_interface(0, usbd_cdc_acm_init_intf(0, &cdc_intf2));
    usbd_add_endpoint(0, &cdc_out_ep);
    usbd_add_endpoint(0, &cdc_in_ep);

    usbd_initialize(0, CONFIG_REG_BASE, usbd_event_handler);
}
